package com.ivo.activiti.dao;

import org.hibernate.query.internal.NativeQueryImpl;
import org.hibernate.transform.Transformers;
import org.springframework.stereotype.Service;

import javax.persistence.*;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.transaction.Transactional;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

/**
 * @author wj
 * @version 1.0
 */
@Service
public class DaoService implements Dao {

    @PersistenceContext
    private EntityManager em;

    @Override
    public <T> T getObject(Class<T> clazz, Serializable id) {
        return em.find(clazz, id);
    }

    @Override
    @Transactional
    public void saveOrUpdateObject(Object o) {
        em.merge(o);
        em.flush();
    }

    @Override
    public <T> T getObject(Class<T> clazz, String propertyName, Object value) {
        CriteriaQuery<T> c = em.getCriteriaBuilder().createQuery(clazz);
        Root<T> root = c.from(clazz);
        Predicate predicate = em.getCriteriaBuilder().equal(root.get(propertyName), value);
        c.where(predicate);
        try {
            return em.createQuery(c).getSingleResult();
        } catch (NoResultException e) {
            return null;
        }
    }

    @Override
    public <T> List<T> find(Class<T> clazz, String propertyName, Object value) {
        CriteriaQuery<T> c = em.getCriteriaBuilder().createQuery(clazz);
        Root<T> root = c.from(clazz);
        Predicate predicate = em.getCriteriaBuilder().equal(root.get(propertyName), value);
        c.where(predicate);
        return em.createQuery(c).getResultList();
    }

    @Override
    public <T> List<T> find(Class<T> clazz, String[] propertyNames, Object[] values) {
        CriteriaQuery<T> c = em.getCriteriaBuilder().createQuery(clazz);
        CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
        Root<T> root = c.from(clazz);
        List<Predicate> predicates = new ArrayList<>();
        for(int i=0; i<propertyNames.length; i++) {
            if(values.length <= i) break;
            predicates.add(criteriaBuilder.equal(root.get(propertyNames[i]), values[i]));
        }
        c.where(predicates.toArray(new Predicate[0]));
        return em.createQuery(c).getResultList();
    }

    @Override
    public <T> List<T> findAll(Class<T> clazz) {
        return em.createQuery("from "+clazz.getName(), clazz).getResultList();
    }

    @Override
    public List findBySql(String sql) {
        Query query = em.createNativeQuery(sql);
        query.unwrap(NativeQueryImpl.class).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
        return query.getResultList();
    }

    @Override
    public <T> List<T> find(String hql, Class<T> clazz, Object... params) {
        TypedQuery query = em.createQuery(hql, clazz);
        for (int i = 0; i < params.length; i++) {
            Object val = params[i];
            query.setParameter(i, val);
        }
        return query.getResultList();
    }
}
